/*
 * This file is part of the Try! Engine project.
 *
 * Copyright 2010-2011 Emanuele Bertoldi. All rights reserved.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * You should have received a copy of the modified BSD License along with this
 * program. If not, see <http://www.opensource.org/licenses/bsd-license.php>
 */

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "projectwizard.h"
#include "aboutdialog.h"
#include <view3d.h>
#include <projectmodellist.h>
#include <QtGui/QMouseEvent>
#include <QtGui/QFileDialog>
#include <QtCore/QPluginLoader>
#include <QtCore/QDirIterator>

#define TRY_SANDBOX_PLUGINS_DIR "plugins"
#define TRY_DEFAULT_MSG_TIMEOUT 5000

using namespace Try;

MainWindow::MainWindow(QWidget* parent)
    :   QMainWindow(parent),
        m_ui(new Ui::MainWindow),
        m_currentProject(0)
{
    m_ui->setupUi(this);

    m_baseTitle = this->windowTitle();

    View3d* view3d = new View3d(this);
    this->setCentralWidget(view3d);

    QObject::connect(m_ui->actionExit, SIGNAL(triggered()), qApp, SLOT(quit()));
    QObject::connect(m_ui->actionNewProject, SIGNAL(triggered()), this, SLOT(newProject()));
    QObject::connect(m_ui->actionOpenProject, SIGNAL(triggered()), this, SLOT(openProject()));
    QObject::connect(m_ui->actionSaveProject, SIGNAL(triggered()), this, SLOT(saveProject()));
    QObject::connect(m_ui->actionSaveProjectAs, SIGNAL(triggered()), this, SLOT(saveProjectAs()));
    QObject::connect(m_ui->actionCloseProject, SIGNAL(triggered()), this, SLOT(closeProject()));
    QObject::connect(m_ui->actionFullscreen, SIGNAL(triggered(bool)), this, SLOT(toggleFullScreen(bool)));
    QObject::connect(m_ui->actionAbout, SIGNAL(triggered()), this, SLOT(about()));

    QObject::connect(ProjectModelList::instance(), SIGNAL(projectLoaded(Project*)), this, SLOT(onProjectLoaded(Project*)));
    QObject::connect(ProjectModelList::instance(), SIGNAL(projectSaved(Project*)), this, SLOT(onProjectSaved(Project*)));
    QObject::connect(ProjectModelList::instance(), SIGNAL(projectClosed(Project*)), this, SLOT(onProjectClosed(Project*)));
    QObject::connect(ProjectModelList::instance(), SIGNAL(projectChanged(Project*)), this, SLOT(onProjectChanged(Project*)));

    this->loadPlugins();
}

MainWindow::~MainWindow()
{
    this->closeProject();

    this->unloadPlugins();

    delete m_ui;
}

void MainWindow::newProject()
{
    ProjectWizard dlg(this);

    if (dlg.exec() == QDialog::Accepted)
    {
        this->closeProject();

        QString name = dlg.name();
        QString description = dlg.description();
        QString author = dlg.author();

        m_currentProject = new Project(name, description, author, QStringList(), this);
    }
}

void MainWindow::openProject()
{
    QFileDialog dlg(this, tr("Open Project"), "", "*.try");

    if (dlg.exec() == QDialog::Accepted)
    {
        QString filename = dlg.selectedFiles().first();

        if (!filename.isEmpty())
        {
            this->closeProject();

            m_currentProject = new Project();
            m_currentProject->setParent(this);
            m_currentProject->load(filename);
        }
    }
}

void MainWindow::saveProject(const QString& filename)
{
    if (m_currentProject)
    {
        if (m_currentProject->filename().isEmpty() && filename.isEmpty())
        {
            this->saveProjectAs();
        }
        else
        {
            m_currentProject->save(filename);
        }
    }
}

void MainWindow::saveProjectAs()
{
    if (m_currentProject)
    {
        QString name = m_currentProject->name();

        QFileDialog dlg(this, tr("Save Project As..."), name, "*.try");

        dlg.setFileMode(QFileDialog::AnyFile);
        dlg.setAcceptMode(QFileDialog::AcceptSave);
        dlg.setDefaultSuffix("try");

        if (dlg.exec() == QDialog::Accepted)
        {
            QString filename = dlg.selectedFiles().first();
            this->saveProject(filename);
        }
    }
}

void MainWindow::closeProject()
{
    if (m_currentProject)
    {
        delete m_currentProject;
        m_currentProject = 0;
    }
}

void MainWindow::toggleFullScreen(bool fullscreen)
{
    if (fullscreen)
        this->showFullScreen();
    else
        this->showMaximized();
}

void MainWindow::about()
{
    AboutDialog about(this);

    about.exec();
}

bool MainWindow::loadPlugin(Plugin* plugin)
{
    if (m_plugins.contains(plugin))
        return true;

    if (plugin && plugin->initialize(this))
    {
        m_plugins.append(plugin);

        return true;
    }

    return false;
}

bool MainWindow::unloadPlugin(int index)
{
    if (index >= m_plugins.count())
        return false;

    Plugin* plugin = m_plugins.takeAt(index);

    if (plugin && plugin->shutdown())
    {
        delete plugin;

        return true;
    }

    return false;
}

void MainWindow::loadPlugins()
{
    QStringList filters;
    filters.append("*.so");
    filters.append("*.dll");

    QDirIterator dir_walker(TRY_SANDBOX_PLUGINS_DIR, filters, QDir::Files, QDirIterator::Subdirectories);

    while (dir_walker.hasNext())
    {
        dir_walker.next();

        if (dir_walker.fileInfo().isFile())
        {
            QString file_path = dir_walker.filePath();

            qDebug("Loading library %s...", file_path.toStdString().c_str());

            QPluginLoader loader(file_path);

            Plugin* plugin = qobject_cast<Plugin*>(loader.instance());

            if (this->loadPlugin(plugin))
                qDebug("Plugin loaded: %s", plugin->name().toStdString().c_str());
            else
                qDebug("Error loading plugin: %s", loader.errorString().toUtf8().data());
        }
    }
}

void MainWindow::unloadPlugins()
{
    int count = m_plugins.count();

    for (int i=count; i>0; i--)
        this->unloadPlugin(i-1);
}

void MainWindow::onProjectLoaded(Project* project)
{
    if (!m_currentProject || m_currentProject == project)
    {
        m_ui->actionSaveProject->setEnabled(true);
        m_ui->actionSaveProjectAs->setEnabled(true);
        m_ui->actionCloseProject->setEnabled(true);

        QString name = project->name();
        this->statusBar()->showMessage(tr("Project [%1] loaded.").arg(name), TRY_DEFAULT_MSG_TIMEOUT);
        this->setWindowTitle(QString("%1 - %2").arg(name).arg(m_baseTitle));
    }
}

void MainWindow::onProjectSaved(Project* project)
{
    if (m_currentProject == project)
    {
        QString name = project->name();
        this->statusBar()->showMessage(tr("Project [%1] saved.").arg(name), TRY_DEFAULT_MSG_TIMEOUT);
    }
}

void MainWindow::onProjectClosed(Project* project)
{
    if (m_currentProject == project)
    {
        m_ui->actionSaveProject->setEnabled(false);
        m_ui->actionSaveProjectAs->setEnabled(false);
        m_ui->actionCloseProject->setEnabled(false);

        QString name = project->name();
        this->statusBar()->showMessage(tr("Project [%1] closed.").arg(name), TRY_DEFAULT_MSG_TIMEOUT);
        this->setWindowTitle(m_baseTitle);
    }
}

void MainWindow::onProjectChanged(Project* project)
{
    if (m_currentProject == project)
        this->setWindowTitle(QString("%1 - %2").arg(project->name()).arg(m_baseTitle));
}
